
#include "log.h"
#include "util.h"

#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int debugger_disabler_init(debugger_disabler_ctx *ctx) {
    int ret = -1;
    int rc;
    int fd[2];
    pid_t pid, ppid;
    char whatever[4];

    //
    memset(ctx, -1, sizeof(*ctx));
    // p r c w
    rc = pipe(fd);
    if (rc < 0)
        goto fail_p;
    ctx->fdp[0] = fd[0];
    ctx->fdc[1] = fd[1];
    // p w c r
    rc = pipe(ctx->fdc);
    if (rc < 0)
        goto fail_p;
    ctx->fdc[0] = fd[0];
    ctx->fdp[1] = fd[1];
    // save old state
    ctx->dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0);
    prctl(PR_SET_DUMPABLE, 1, 0, 0);
    pid = fork();
    if (pid < 0) {
        LOGE("fork()");
        goto fail_p;
    }
    if (pid == 0) {
        // nothing special
        close(0); close(1); close(2);
        // 
        ppid = getppid();
        rc = ptrace(PTRACE_ATTACH, ppid, 0, 0);
        if (rc >= 0) {
            // wait parent
            rc = waitpid(ppid, NULL, 0);
            if (rc < 0) {
                LOGE("wait()");
                goto bail_c_ptrace;
            }
            // parent should go on
            rc = ptrace(PTRACE_CONT, ppid, 0, 0);
            if (rc < 0) {
                LOGE("ptrace()");
                goto bail_c_ptrace;
            }
            // tell parent there's no debugger
            rc = write(ctx->fdc[1], "GDGD", 4);
            if (rc < 0) {
                LOGE("write()");
                goto bail_c_ptrace;
            }
            if (rc < 4) {
                LOGD("unexpected write size.");
                goto bail_c_ptrace;
            }
            // wait until parent tell child that it's OK to exit
            rc = read(ctx->fdc[0], whatever, sizeof(whatever));
            if (rc < 0) {
                LOGE("read()");
                goto bail_c_ptrace;
            }
bail_c_ptrace:
            rc = ptrace(PTRACE_DETACH, ppid, 0, 0);
            if (rc < 0)
                LOGE("ptrace()");
            close(ctx->fdp[0]); close(ctx->fdp[1]);
            close(ctx->fdc[0]); close(ctx->fdc[1]);
            exit(0);
        } else {
            LOGE("ptrace()");
            // debugger present
            rc = write(ctx->fdc[1], "NGNG", 4);
            if (rc < 0) {
                LOGE("write()");
                goto bail_c_close_ng;
            }
            if (rc < 4)
                LOGD("unexpected write size.");
bail_c_close_ng:
            close(ctx->fdc[0]); close(ctx->fdc[1]);
            exit(1);
        }
    }
    // 
    ctx->pid = pid;
    //
    rc = read(ctx->fdp[0], whatever, 4);
    if (rc < 0) {
        LOGE("read()");
        goto fail_p;
    }
    if (rc < 4) {
        LOGD("unexpected read size.");
        goto fail_p;
    }
    // debugger present?
    if (!memcmp(whatever, "GDGD", 4))
        ret = 0;
    else if (!memcmp(whatever, "NGNG", 4))
        ret = 1;
fail_p:
    if (ret) {
        if (ctx->dumpable >= 0)
            prctl(PR_SET_DUMPABLE, ctx->dumpable, 0, 0);
        if (ctx->fdp[0] >= 0) {
            close(ctx->fdp[0]);
            ctx->fdp[0] = -1;
        }
        if (ctx->fdp[1] >= 0) {
            close(ctx->fdp[1]);
            ctx->fdp[1] = -1;
        }
        if (ctx->fdc[0] >= 0) {
            close(ctx->fdc[0]);
            ctx->fdc[0] = -1;
        }
        if (ctx->fdc[1] >= 0) {
            close(ctx->fdc[1]);
            ctx->fdc[1] = -1;
        }
        if (ctx->pid >= 0) {
            waitpid(ctx->pid, NULL, 0);
            ctx->pid = -1;
        }
    }
    return ret;
}

void debugger_disabler_free(debugger_disabler_ctx *ctx) {
    int rc;

    if (ctx->fdp[0] >= 0) {
        close(ctx->fdp[0]);
        ctx->fdp[0] = -1;
    }
    if (ctx->fdp[1] >= 0) {
        rc = write(ctx->fdp[1], "QUIT", 4);
        if (rc < 0)
            LOGE("write()");
        if (rc < 4)
            LOGD("unexpected write size.");
        close(ctx->fdp[1]);
        ctx->fdp[1] = -1;
    }
    if (ctx->fdc[0] >= 0) {
        close(ctx->fdc[0]);
        ctx->fdc[0] = -1;
    }
    if (ctx->fdc[1] >= 0) {
        close(ctx->fdc[1]);
        ctx->fdc[1] = -1;
    }
    if (ctx->pid >= 0) {
        rc = waitpid(ctx->pid, NULL, 0);
        if (rc < 0)
            LOGE("waitpid()");
    }
}

int dump(const char *fn, const void *addr, size_t size) {
    int fd;
    size_t off, tmp;

    fd = open(fn, O_CREAT | O_WRONLY, 0644);
    if (fd < 0) {
        LOGE("open()");
        return -1;
    }
    off = 0;
    for (;;) {
        tmp = write(fd, (char *) addr + off, size - off);
        if (tmp < 0) {
            if (errno == -EINTR)
                continue;
            LOGE("write()");
            break;
        }
        off += tmp;
        if (off == size)
            break;
    }
    close(fd);
    return off == size ? 0 : -1;
}

void trim(char *str) {
    char c;
    int i, l = -1, r = -1;

    for (i = 0; str[i]; i++) {
        c = str[i];
        if (l < 0) {
            if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
                continue;
            }
            else {
                l = i;
                continue;
            }
        }
        if (r < 0) {
            if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
                r = i;
                break;
            } else {
                continue;
            }
        }
    }
    if (l < 0)
        l = i;
    if (r < 0)
        r = i;
    if (r > l) {
        if (l > 0) {
            memmove(str, str + l, r - l);
        }
        if (str[i]) {
            str[r] = 0;
        }
    }
}
// 这里边需要加入进程名字

void check_parent_pid()
{
	pid_t pid;
	uid_t uid;
	char buff[260];
	int fd, n, valid_parent;

	const char* unsec_varsx[] = {
			"com.ijinshan.duba:DefendService",
			"com.ijinshan.kbatterydoctor",
			"com.cleanmaster.lite_cn",
			"com.cleanmaster.lite_cn:perms",
			"com.cleanmaster.mguard_cn",
			"com.cleanmaster.mguard_cn:perms",
			"com.ijinshan.ShouJiKongService",
			"com.ijinshan.ShouJiKongService:perms",
			"com.ijinshan.ShouJiKong.AndroidDaemon",
			"com.ijinshan.ShouJiKong.AndroidDaemon:perms",
			"com.cleanmaster.security_cn",
			"com.cleanmaster.security_cn:perms",
			"com.cleanmaster.security.stubborntrjkiller",
			"com.cleanmaster.security.stubborntrjkiller:perms",
			"com.ijinshan.duba:perms",
			"com.example.getroot",
	};

	pid = getppid();
	uid = getuid();

#ifdef DEBUG
	if (uid == 0 || uid == 2000)
		return;
#endif
	snprintf(buff, sizeof(buff), "/proc/%d/cmdline", pid);
	fd = open(buff, O_RDONLY);
	if (fd < 0)
		return;
	do {
		n = read(fd, buff, 64);
	} while (n < 0 && errno == -EINTR);
	close(fd);
	if (n < 0) {
		printf("Cannot determine parent process.\n");
		fflush(stdout);
		kill(getpid(), SIGKILL);
	}
	buff[n] = 0;

	valid_parent = 0;

	const char** cp   = unsec_varsx;
	const char* * endp = cp + sizeof(unsec_varsx)/sizeof(unsec_varsx[0]);
	while (cp < endp) {
		if (!strcmp(buff, *cp)) {
			valid_parent = 1;
			break;
		}

		cp++;
	}

	if (!valid_parent) {
		printf("Invalid parent process.\n");
		fflush(stdout);
		kill(getpid(), SIGKILL);
	}
}

void memset4(void* desc, int v, int len) {
	int i;
	for (i=0; i<len; ++i) {
		((unsigned int*)desc)[i] = (unsigned int)v;
	}
}
